#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <functional>
#include <fstream>
#include <unordered_map>

const static std::string base_sep = "\r\n";
const static std::string line_sep = ": ";
const static std::string prefixpath = "wwwroot"; //路径前缀，web根目录
const static std::string homepage = "index.html";//主页
const static std::string httpversion = "HTTP/1.0";
const static std::string spacesep = " ";
const static std::string suffixsep = ".";//后缀片段
const static std::string html_404 = "404.html";//404网页
const static std::string arg_sep = "?";//参数片段

class HttpRequest
{
private:
    // \r\n
    // \r\ndata
    //获取一行
    std::string GetLine(std::string &reqstr)
    {
        auto pos = reqstr.find(base_sep);
        if (pos == std::string::npos)
            return std::string();//没找到base_sep，可能是没有下一行了，返回空
        std::string line = reqstr.substr(0, pos);
        reqstr.erase(0, line.size() + base_sep.size());//读取过的数据都清空
        return line.empty() ? base_sep : line;
    }
    void ParseReqLine()
    {
        std::stringstream ss(_req_line);   // cin >>
        ss >> _method >> _url >> _version; // /a/b/c.html or /login?user=XXX&passwd=1234 /register

        if (strcasecmp(_method.c_str(), "GET") == 0)//C语言函数，比较时不区分大小写
        {
            auto pos = _url.find(arg_sep);
            if (pos != std::string::npos)
            {
                _body_text = _url.substr(pos + arg_sep.size());//获得查询字符串，由若干个键值对构成，&分隔
                _url.resize(pos);//重新设置大小，去除后面的查询字符串，只保留了前面的网络路径
            }
        }

        _path += _url;//前缀+_url==请求资源在server中的路径

        if (_path[_path.size() - 1] == '/')
        {   
            //最后一个位置是'/'，说明_path是web根目录，此时拼上主页路径
            _path += homepage;
        }

        // wwwroot/index.html
        // wwwroot/image/1.png
        auto pos = _path.rfind(suffixsep);//从后面找后缀
        if (pos != std::string::npos)
        {
            _suffix = _path.substr(pos);//获得请求的资源文件后缀
        }
        else
        {
            _suffix = ".default";//没找到，默认
        }
    }

    void ParseReqHeader()
    {
        for (auto &header : _req_headers)
        {
            auto pos = header.find(line_sep);//line_sep=": ";
            if (pos == std::string::npos)
                continue;//没找到分隔符
            std::string k = header.substr(0, pos);
            std::string v = header.substr(pos + line_sep.size());
            if (k.empty() || v.empty())
                continue;//找到了，但k或v为空，有错误
            //找到了，并且kv有效
            _headers_kv.insert(std::make_pair(k, v));
        }
    }

public:
    HttpRequest() : _blank_line(base_sep), _path(prefixpath)
    {
    }
    void Deserialize(std::string &reqstr)
    {
        // 基本的反序列化
        //获取第一行（请求行）
        _req_line = GetLine(reqstr);
        std::string header;
        //获取多行请求报头，放入请求报头表中
        do
        {
            header = GetLine(reqstr);
            if (header.empty())
                break;
            else if (header == base_sep)
                break;//得到的是空行，说明下一行就是正文了
            _req_headers.push_back(header);
        } while (true);
        //判断剩下的字符---正文部分是否为空
        if (!reqstr.empty())
        {
            _body_text = reqstr;//获得正文
        }

        // 在进一步反序列化
        ParseReqLine();//分析请求行
        ParseReqHeader();//分析若干行请求报头
    }
    std::string Url()
    {
        LOG(DEBUG, "Client Want url %s\n", _url.c_str());
        return _url;
    }
    std::string Path()
    {
        LOG(DEBUG, "Client Want path %s\n", _path.c_str());
        return _path;
    }
    std::string Suffix()
    {
        return _suffix;
    }
    std::string Method()
    {
        LOG(DEBUG, "Client request method is %s\n", _method.c_str());
        return _method;
    }
    std::string GetRequestBody()
    {
        LOG(DEBUG, "Client request method is %s, args: %s, request path: %s\n",
            _method.c_str(), _body_text.c_str(), _path.c_str());
        return _body_text;
    }
    void Print()
    {
        std::cout << "----------------------------" << std::endl;
        std::cout << "###" << _req_line << std::endl;
        for (auto &header : _req_headers)
        {
            std::cout << "@@@" << header << std::endl;
        }
        std::cout << "***" << _blank_line;
        std::cout << ">>>" << _body_text << std::endl;

        std::cout << "Method: " << _method << std::endl;
        std::cout << "Url: " << _url << std::endl;
        std::cout << "Version: " << _version << std::endl;

        for (auto &header_kv : _headers_kv)
        {
            std::cout << ")))" << header_kv.first << "->" << header_kv.second << std::endl;
        }
    }
    ~HttpRequest()
    {
    }

private:
    // 基本的httprequest的格式
    std::string _req_line;//请求行
    std::vector<std::string> _req_headers;//请求报头表
    std::string _blank_line;//空行
    std::string _body_text;//正文

    // 更具体的属性字段，需要进一步反序列化
    std::string _method;//http方法
    std::string _url;//统一资源定位符
    std::string _path;//网络路径
    std::string _suffix; //资源后缀
    std::string _version;//http版本
    std::unordered_map<std::string, std::string> _headers_kv;//报头中的kv值
};

class HttpResponse
{
public:
    HttpResponse() : _verison(httpversion), _blank_line(base_sep)
    {
    }
    void AddCode(int code, const std::string &desc)
    {
        _status_code = code;
        _desc = desc;
    }
    void AddHeader(const std::string &k, const std::string &v)
    {
        _headers_kv[k] = v;
    }
    void AddBodyText(const std::string &body_text)
    {
        _resp_body_text = body_text;
    }
    //序列化
    std::string Serialize()
    {
        // 1. 构建状态行
        _status_line = _verison + spacesep + std::to_string(_status_code) + spacesep + _desc + base_sep;

        // 2. 构建应答报头
        for (auto &header : _headers_kv)
        {
            std::string header_line = header.first + line_sep + header.second + base_sep;
            _resp_headers.push_back(header_line);
        }

        // 3. 空行和正文

        // 4. 正式序列化
        std::string responsestr = _status_line;
        for (auto &line : _resp_headers)
        {
            responsestr += line;
        }
        responsestr += _blank_line;
        responsestr += _resp_body_text;

        return responsestr;
    }
    ~HttpResponse()
    {
    }

private:
    // httpresponse base 属性
    std::string _verison;
    int _status_code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _headers_kv;

    // 基本的httprequest的格式
    std::string _status_line;
    std::vector<std::string> _resp_headers;
    std::string _blank_line;
    std::string _resp_body_text;
};

using func_t = std::function<HttpResponse(HttpRequest&)>;

class HttpServer
{
private:
    //传网络路径，获得文件内容
    std::string GetFileContent(const std::string &path)
    {
        std::ifstream in(path, std::ios::binary);//因为不知道文件是什么格式，以二进制形式打开是最安全的
        if (!in.is_open())
            return std::string();
        in.seekg(0, in.end);//把文件内容指针移到末尾
        int filesize = in.tellg();//告知我你的rw偏移量是多少，单位字节
        in.seekg(0, in.beg);//恢复

        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();

        return content;
    }

public:
    HttpServer()
    {
        //资源文件类型(后缀) 和 http中content_type属性的映射，客户端通过content_type知道接收的文件类型，然后使用对应的格式/方式显示资源
        _mime_type.insert(std::make_pair(".html", "text/html"));
        _mime_type.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(std::make_pair(".png", "image/png"));
        _mime_type.insert(std::make_pair(".default", "text/html"));
        //状态码翻译，状态码 和 含义的映射
        _code_to_desc.insert(std::make_pair(100, "Continue"));
        _code_to_desc.insert(std::make_pair(200, "OK"));
        _code_to_desc.insert(std::make_pair(201, "Created"));
        _code_to_desc.insert(std::make_pair(301, "Moved Permanently"));
        _code_to_desc.insert(std::make_pair(302, "Found"));
        _code_to_desc.insert(std::make_pair(404, "Not Found"));
    }

    // #define TEST
    //http协议中的请求
    std::string HandlerHttpRequest(std::string &reqstr) // req 曾经被客户端序列化过！！！
    {
#ifdef TEST
        std::cout << "---------------------------------------" << std::endl;
        std::cout << reqstr;//接收到对方的请求内容??????
        //用来响应的内容
        std::string responsestr = "HTTP/1.1 200 OK\r\n";
        responsestr += "Content-Type: text/html\r\n";
        responsestr += "\r\n";
        responsestr += "<html><h1>hello Linux, hello bite!</h1></html>";

        return responsestr;
#else
        std::cout << "---------------------------------------" << std::endl;
        std::cout << reqstr;//接收到对方的请求内容??????
        std::cout << "---------------------------------------" << std::endl;
        //创建请求+响应对象
        HttpRequest req;
        HttpResponse resp;
        req.Deserialize(reqstr);//反序列化，解包请求报文
        // req.Method();//方法
        
        //接收到的请求对象的Path == "wwwroot/redir"
        if (req.Path() == "wwwroot/redir")
        {
            // 处理重定向
            std::string redir_path = "https://www.qq.com";
            //在响应对象中添加状态码+描述
            // resp.AddCode(302, _code_to_desc[302]);
            resp.AddCode(301, _code_to_desc[301]);
            //在响应对象中设置要给对方的web路径
            resp.AddHeader("Location", redir_path);
        }
        //接收到的请求信息的data不为空
        else if(!req.GetRequestBody().empty())
        {
            //判断server是否存在对方请求的这个web路径资源/服务
            if(IsServiceExists(req.Path()))
            {
                //在服务列表中找到对应路径，并传请求对象去调用登录函数，返回相应对象
                resp = _service_list[req.Path()](req);
            }   
        }
        //对方的请求对象中Path不是默认的/首页，data为空
        else
        {
            // 最基本的上层处理,处理静态资源
            // 获取请求对象中Path文件的内容
            std::string content = GetFileContent(req.Path());
            if (content.empty())//如果没找到/内容为空
            {
                //设置响应给对方的错误信息（报文）
                //获取"wwwroot/404.html"内容
                content = GetFileContent("wwwroot/404.html");
                //设置状态码+描述
                resp.AddCode(404, _code_to_desc[404]);
                //设置报头信息包括 正文大小+正文类型
                resp.AddHeader("Content-Length", std::to_string(content.size()));
                resp.AddHeader("Content-Type", _mime_type[".html"]);
                //设置正文内容
                resp.AddBodyText(content);
            }
            //获得了Path文件内容
            else
            {   
                //设置响应给对方的信息（报文）
                resp.AddCode(200, _code_to_desc[200]);
                resp.AddHeader("Content-Length", std::to_string(content.size()));
                resp.AddHeader("Content-Type", _mime_type[req.Suffix()]);
                resp.AddHeader("Set-Cookie", "username=zhangsan");
                // resp.AddHeader("Set-Cookie", "passwd=12345");
                resp.AddBodyText(content);
            }
        }
        //序列化响应信息+返回
        return resp.Serialize();
#endif
    }
    //填充服务列表中服务路径(文件)对应的服务函数
    void InsertService(const std::string &servicename, func_t f)
    {
        std::string s = prefixpath + servicename;
        _service_list[s] = f;
    }
    bool IsServiceExists(const std::string &servicename)
    {
        auto iter = _service_list.find(servicename);
        if(iter == _service_list.end()) return false;
        else return true;
    }
    ~HttpServer() {}

private:
//Multipurpose Internet Mail Extensions type即多用途互联网邮件扩展类型，
//是一种标准化的方式来表示文档、文件或数据的性质和格式，在互联网通信和计算机领域中有着广泛的应用
    std::unordered_map<std::string, std::string> _mime_type;
    std::unordered_map<int, std::string> _code_to_desc;
    std::unordered_map<std::string, func_t> _service_list;
};